/*
 * CMapMatrix.h
 *
 *  Created on: 23.12.2012
 *      Author: caffeine
 */

#ifndef __CMAPMATRIX_H__
#define __CMAPMATRIX_H__

#include <lampe/types.h>
#include <lampe/IReferenceCounted.h>
#include <lampe/exception/OutOfBounds.h>
#include "MatrixRow.h"

namespace lampe{

/***
 * @class IMapMatrix2d
 * Класс, реализующий 2d матрицу
 ***/
template <typename T>
class IMapMatrix2d:public virtual IReferenceCounted{
private:
	u32 w;
	u32 h;
	T*map;
public:


	/***
	 * Конструктор. Создаёт матрицу указанного размера
	 *
	 * @param width ширена матрицы
	 * @param height высота матрицы
	 ***/
	IMapMatrix2d(u32 width,u32 height){
		map=0;
		w=0;
		h=0;
		recreate(width,height);
	}

	/***
	 * Копирует в текущую матрицу содержание указанной
	 *
	 * @param matrix матрица, содержание которой нужно скопировать в текущую
	 * **/
	void copy(IMapMatrix2d<T>*matrix){
		recreate(matrix->width(),matrix->height());
		memcpy(&map[0],&matrix->map[0],width()*height()*sizeof(T));
	}

	IMapMatrix2d(){
		map=0;
		w=0;
		h=0;
	}

	/***
	 * Возвращяет количество значений в матрице
	 *
	 * @return количество значений в матрице
	 * **/
	u32 count(){
		return width()*height();
	}

	/***
	 * Возвращяет размер матрица данных в байтах
	 *
	 * @return размер данных матрицы в байтах
	 * **/
	u32 size(){
		return count()*sizeof(T);
	}

	/***
	 * Заполняет каждую ячейку массива указанным значением
	 *
	 * @param value значение, которое нужно записать
	 * **/
	void clear(T value){
		printf("Count=%u\n",count());
		u32 co=count();
		for (u32 i=0;i<co-1;i++){
			printf("I=%u\n",i);
			map[i]=value;
		}
	}

	~IMapMatrix2d(){
		recreate(0,0);
	}

	void recreate(u32 width,u32 height){
		if (map && w>0 && h>0)
			free(map);
		w=width;
		h=height;
		if (w>0 && h>0){
			void*pointer=malloc(w*h*sizeof(T));
			if (!pointer)
				printf("ERROR!!! Can't malloc memory!!!\n");
			map=(T*)pointer;
		}else{
			map=0;
			w=0;
			h=0;
		}
	}

	MatrixRow<T> operator [](u32 i){
		return MatrixRow<T>(&map[i*w],height());
	}

	/***
	 * Возвращяет ширину матрицы
	 *
	 * @return ширена матрицы
	 ***/
	u32 width(){
		return w;
	}

	/***
	 * Возвращяет высоту матрицы
	 *
	 * @return высота матрицы
	 ***/
	virtual u32 height(){
		return h;
	}

	bool getIndex(u32 x,u32 y,u32*index){
		if (!index){
			return false;
		}
		if (x>=width() || y>=height()){
			return false;
		}
		*index=y*width()+x;
		return true;
	}

	bool getCoords(u32 index,u32*x,u32*y){
		if (!x && !y){
			return false;
		}
		if (index>=count()){
			return false;
		}
		u32 t_y=floor(index/w);
		if (y)
			*y=t_y;
		if (x)
			*x=index-t_y*w;
		return true;
	}

	/***
	 * Устанавливает значение ячейки
	 *
	 * @param x столбец матрицы
	 * @param y строка матрицы
	 * @param value устанавливаемое значение ячейки матрицы
	 ***/
	void set(u32 x,u32 y,T value){
		if (x>=width() || y>=height())
			GEN_OutOfBounds_ERROR(StringUtils::format("Can't set coordinat %u;%u",x,y));
		map[y*w+x]=value;
	}

	/***
	 * Устанавливает значение ячейки
	 *
	 * @param coord координаты в матрице
	 * @param value устанавливаемое значение ячейки матрицы
	 ***/
	void set(lampe::Vector2du coord,T value){
		set(coord.X,coord.Y,value);
	}

	/***
	 * Возвращяет значение ячейки матрицы
	 *
	 * @param x столбец матрицы
	 * @param y строка матрицы
	 * @return значение ячейки матрицы
	 ***/
	T get(u32 x,u32 y){
		if (x>=width() || y>=height())
			GEN_OutOfBounds_ERROR(StringUtils::format("Can't give coordinat %u;%u",x,y));
		return map[y*w+x];
	}

	/***
	 * Возвращяет значение ячейки матрицы
	 *
	 * @param coord координаты в матрице
	 * @return значение ячейки матрицы
	 ***/
	T get(lampe::Vector2du coord){
		return get(coord.X,coord.Y);
	}
};

}//namespace lampe
#endif /* __IMAPMATRIX_H__ */
